package com.lzy.wzy.netty;


//import com.lzy.wzy.netty.code.WsFileHandler;
import com.lzy.wzy.netty.handler.WebSocketHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
@Slf4j
public class NettyServer {

    /*
        * webSocket协议名
     */
    private static final String WEBSOCKET_PROTOCOL = "WebSocket";

    private final String path="/ws";

    EventLoopGroup boosGroup;

    EventLoopGroup workGroup;

    @Autowired
    private WebSocketHandler webSocketHandler;


//    @Autowired
//    private WsFileHandler wsFileHandler;
    /*
        启动
     */

    public void start() throws Exception{
        boosGroup=new NioEventLoopGroup(1);
        workGroup=new NioEventLoopGroup(2);

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boosGroup,workGroup)
                .channel(NioServerSocketChannel.class)
//                .option(ChannelOption.SO_REUSEADDR,true)
                .option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576))
                .childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 流水线管理通道中的处理程序（Handler），用来处理业务
                        // webSocket协议本身是基于http协议的，所以这边也要使用http编解码器
                        ch.pipeline().addLast(new HttpServerCodec());

                        //ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
//                        ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
//                        ch.pipeline().addLast(new ObjectEncoder());
                        // 以块的方式来写的处理器
                        ch.pipeline().addLast(new ChunkedWriteHandler());
                /*
                请求分段，聚合请求进行完整的请求或响应
                说明：
                1、http数据在传输过程中是分段的，HttpObjectAggregator可以将多个段聚合
                2、这就是为什么，当浏览器发送大量数据时，就会发送多次http请求
                 */
//                        ch.pipeline().addLast(new HttpObjectAggregator(8192));

                        //对httpMessage进行聚合,聚合成FullHttpReq 或者 FullHttpRes
                        ch.pipeline().addLast(new HttpObjectAggregator(1024*64));
                        //websocket服务器协议,用于指定给客户端链接路由:ws,会帮忙处理握手动作,以及心跳
                        //处理心跳检测
                        //ch.pipeline().addLast(new IdleStateHandler(6*10,20,0));

                /*
                说明：
                1、对应webSocket，它的数据是以帧（frame）的形式传递
                2、浏览器请求时 ws://localhost:8000/xxx 表示请求的uri
                3、核心功能是将http协议升级为ws协议，保持长连接
                */
                        ch.pipeline().addLast(new WebSocketServerProtocolHandler(path, WEBSOCKET_PROTOCOL, true, 65536 * 10));

                        // 自定义的handler，处理业务逻辑
                        ch.pipeline().addLast(webSocketHandler);

//                        ch.pipeline().addLast(wsFileHandler);
                    }
                });
        //启动
        //绑定端口，并设置为同步方式，是一个异步的chanel
        //配置完成，开始绑定server，通过调用sync同步方法阻塞直到绑定成功
        int port = 8990;
        ChannelFuture channelFuture = bootstrap.bind(port).sync();

        log.info("Server started and listen on:{}", channelFuture.channel().localAddress());

        /*
         * 关闭
         * 获取某个客户端所对应的channel，关闭并设置同步方式
         * 对关闭通道进行监听
         */
        channelFuture.channel().closeFuture().sync();
    }

    /*
        在程序关闭前
     * 释放资源
     */
    @PreDestroy
    public void destroy() throws InterruptedException {
        if (boosGroup != null) {
            boosGroup.shutdownGracefully().sync();
        }
        if (workGroup != null) {
            workGroup.shutdownGracefully().sync();
        }
    }

    /*
        在创建Bean时运行
     * 需要开启一个新的线程来执行netty server 服务器
     */
    @PostConstruct()
    public void init(){
        new Thread(() -> {
            try {
                start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }
}
